import pandas as pd # Manipulación y carga de datos.
import plotly.express as px # Gráficos intéractivos.
import numpy as np # Operaciones matemáticas.
import warnings # Ignorar mensajes de advertencia.
warnings.filterwarnings("ignore")
df=pd.read_csv("insurance.csv")
df["bmi"]=df["bmi"].apply(lambda x: 18 if x < 18 else x)
df["bmi"]=df["bmi"].apply(lambda x: 47 if x >47 else x)
px.box(df,x="bmi",title="Eliminate outlires for BMI")
def split_data(condition):
return df.query("smoker=='{}'".format(condition))
def upper_limit(data):
return np.mean(data["charges"])+2*np.std(data["charges"])
smoker_no=split_data("no")
upper_limit(smoker_no)
20416.19735843142
smoker_no["charges"]=smoker_no["charges"].apply(lambda x: np.nan if x > 20000 else x)
((smoker_no.isnull().sum() / len(df))*100).sort_values(ascending = False)
charges 4.559043 age 0.000000 sex 0.000000 bmi 0.000000 children 0.000000 smoker 0.000000 region 0.000000 dtype: float64
def scatter_age(dataframe):
return px.scatter(dataframe,x="age",y="charges",trendline="ols")
scatter_age(smoker_no)
Aún observamos valores atípicos. Por lo cual obtaré por dividir el dataset en base a la edad del usuario. Para una mejor limpieza.
young_adults=smoker_no.query("age<30")
adults=smoker_no.query("age>=30 and age<50")
old_adults=smoker_no.query("age>=50")
def outlires_to_nan(dataframe,upper_limit):
return dataframe["charges"].apply(lambda x: np.nan if x>upper_limit else x)
young_adults["charges"]=outlires_to_nan(young_adults,7000)
adults["charges"]=outlires_to_nan(adults,10000)
old_adults["charges"]=outlires_to_nan(old_adults,18000)
smoker_no=pd.concat([young_adults,adults,old_adults])
scatter_age(smoker_no)
La gráfica ignora los valores atípicos. Sólo muestra los datos limpios. Ahora sólo tenemos que reemplazar los valores pérdidos.
Obtaré por creear un modelo de regresión lineal. Para reemplazar dichos valores. Ya que los variable de la edad y cargo del seguro. Tiene una tendencia lineal para los usuarios que no fuman.
smoker_no_clear=smoker_no.dropna()
smoker_no_nan=smoker_no[smoker_no["charges"].isnull()]
from sklearn.pipeline import Pipeline
from sklearn.compose import make_column_transformer # Preporcesamiento de las varaibles.
X=smoker_no_clear.drop(["charges","smoker"],axis="columns")
y=smoker_no_clear.charges
from sklearn.model_selection import train_test_split,cross_val_score
X_train,X_test,Y_train,Y_test=train_test_split(X,y,test_size=0.33,random_state=0)
X_train.shape,X_test.shape
((636, 5), (314, 5))
El primer método es utilizado para varaibles categóricas en la mayoria de los casos. Tiene una ventaja a nivel geometrico que la distancia entre ambas variables ficticas es igual.
El segundo método es usado exclusivamente para varaiables numéricas. Es un reajsute de escalas. Para que las varaibles sean comparables entre si. Los datos quedan es una escala de números de 0 a 1 como máximo.
La fórmula es la siguiente : $x-min(x)/max(x)-min(x)$
from sklearn.preprocessing import OneHotEncoder,MinMaxScaler
tf_col=make_column_transformer((MinMaxScaler(),["age","bmi","children"]),
(OneHotEncoder(drop="if_binary"),["sex"]))
from sklearn.linear_model import LinearRegression
lm=Pipeline([("preprocessor",tf_col),("linear_model",LinearRegression())])
lm.fit(X_train,Y_train)
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('minmaxscaler',
MinMaxScaler(),
['age', 'bmi', 'children']),
('onehotencoder',
OneHotEncoder(drop='if_binary'),
['sex'])])),
('linear_model', LinearRegression())])
lm.score(X_train,Y_train)
0.9748528429981995
lm.score(X_test,Y_test)
0.978056938616521
cross_val_score(lm,X_test,Y_test,cv=5).mean()
0.977561264582439
El valor de generalización fue bastente alto. Por cual el modelo lineal puede describir al 97 de las observaciones. Podemos usarlo para sustituir los valores pérdidos y así evitar la pérdida excesiva de datos.
new_data=smoker_no_nan.drop(["charges"],axis="columns")
smoker_no_nan["charges"]=lm.predict(new_data)
smoker_no_nan.head()
| age | sex | bmi | children | smoker | region | charges | |
|---|---|---|---|---|---|---|---|
| 102 | 18 | female | 30.115 | 0 | no | northeast | 1102.512311 |
| 143 | 29 | male | 29.735 | 2 | no | northwest | 4445.529529 |
| 219 | 24 | female | 23.210 | 0 | no | southeast | 2723.957387 |
| 291 | 29 | male | 29.640 | 1 | no | northeast | 4000.677206 |
| 305 | 29 | male | 33.345 | 2 | no | northwest | 4433.077319 |
smoker_no_nan.isnull().sum()
age 0 sex 0 bmi 0 children 0 smoker 0 region 0 charges 0 dtype: int64
Observamos que exitosamente logramos sustituir los valores pérdidos.
smoker_no_clear=pd.concat([smoker_no_clear,smoker_no_nan])
smoker_yes=split_data("yes")
px.histogram(smoker_yes,x="charges")
Sustituiremos los valores donde el cargo del seguro sea superior a $50000 dólares.
smoker_yes["charges"]=smoker_yes["charges"].apply(lambda x: 48000 if x > 48000 else x)
smoker_yes_clear=smoker_yes
df_clear=pd.concat([smoker_no_clear,smoker_yes_clear])
df_clear.to_csv("insurence_clear")